//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.server;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import tv.dyndns.kishibe.client.Constant;
import tv.dyndns.kishibe.client.packet.PacketGameStatus;
import tv.dyndns.kishibe.client.packet.PacketMatchingPlayer;
import tv.dyndns.kishibe.client.packet.PacketProblemData;
import tv.dyndns.kishibe.client.packet.PacketResult;

//どう考えてもステートパターンなのだが・・・
//良い構造が思いつかない・・・orz
public class Game {
	private final GameManager gameManager;
	private static volatile int playerId = 0;
	private volatile int classLevel;
	private volatile int sessionId;
	private volatile int transition = Constant.TRANSITION_MATCHING;// 現在の遷移状態
	private volatile int restMatchingSecond;// マッチング終了までの秒数
	private final List<Integer> problemIDs = new ArrayList<Integer>(); // 問題データ等
	private volatile PacketProblemData[] problems = null;
	private final Set<Integer> selectedProblemIds = new HashSet<Integer>();
	private final List<PlayerStatus> arrayPlayerStatus = new ArrayList<PlayerStatus>();
	private volatile int numberOfHumanPlayer = 0;
	private volatile int initialNumberOfHumanPlayers = 0;
	private final Set<Integer> setGenre = new HashSet<Integer>(); // プレイヤーを追加する
	private final Set<Integer> setType = new HashSet<Integer>();
	private final Set<Integer> setDifficult = new HashSet<Integer>();
	private volatile int numberOfRequestStartingGame = 0; // 直ちにゲームを開始するように迫る
	private final List<PacketMatchingPlayer> matchingPlayers = new ArrayList<PacketMatchingPlayer>(); // マッチング情報を返す
	private volatile int restWaitForGameSecond; // ゲーム待機状態への遷移
	private volatile int problemCounter = 0;
	private volatile int answerCounter = 0;
	private volatile long questionStartTime;
	private final Random random = new Random();
	private volatile PacketResult[] packetResult; // 最終結果を返す
	private volatile Timer timerMatching = new Timer();
	private volatile String[] comIconFileNames;
	private static final String[] ACCEPT_EXTENSIONS = { ".jpg", ".jpeg", ".gif", ".png", ".bmp" };
	private volatile ComputerPlayer computerPlayer = null;
	private final Object lock = new Object();
	private final boolean event;
	private final boolean alone;
	private volatile int firstGenre;
	private volatile int firstType;
	private volatile int firstDifficultSelect;
	private volatile Timer timerReadyForGame = new Timer();
	private volatile Timer timerQuestion;
	private volatile Timer timerEndAnswer;
	private final String theme;
	private final boolean publicEvent;

	public Game(GameManager gameManager, int sessionId, int classLevel, boolean event, boolean alone, String theme, boolean publicEvent) {
		this.gameManager = gameManager;
		this.sessionId = sessionId;
		this.classLevel = classLevel;
		this.event = event;
		this.alone = alone;
		this.theme = theme;
		this.publicEvent = publicEvent;

		// アイコンファイル名リスト初期化
		comIconFileNames = new File(Constant.ICON_FOLDER_PATH).list(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				for (String extension : ACCEPT_EXTENSIONS) {
					if (name.contains(extension)) {
						return true;
					}
				}

				return false;
			}
		});
		if (comIconFileNames == null || comIconFileNames.length == 0) {
			final String[] iconFileNames = { Constant.ICON_NO_IMAGE };
			comIconFileNames = iconFileNames;
		}

		// マッチング終了タイマー開始
		restMatchingSecond = Constant.WAIT_SECOND_FOR_MATCHING;
		timerMatching.schedule(new TimerTask() {
			public void run() {
				synchronized (this) {
					// タイマーをひとつ減少させる
					if (--restMatchingSecond < 0) {
						// ゲーム待機へ以降
						transToReadyForGame();
					}
				}
			}
		}, 0, 1000);
	}

	public int getRestMatchingSecond() {
		synchronized (lock) {
			return restMatchingSecond;
		}
	}

	public void setTransition(int transition) {
		synchronized (lock) {
			this.transition = transition;
		}
	}

	public int getTransition() {
		synchronized (lock) {
			return transition;
		}
	}

	public int getNumberOfHumanPlayer() {
		synchronized (lock) {
			return numberOfHumanPlayer;
		}
	}

	public int getNumberOfPlayer() {
		synchronized (lock) {
			return arrayPlayerStatus.size();
		}
	}

	public PacketProblemData[] getProblemData() {
		synchronized (lock) {
			return problems;
		}
	}

	public PlayerStatus addPlayer(String name, int genre, int type, String greeting, String imageFileName, int classLevel, int difficultSelect, int rating, int userCode, boolean newProblem) {
		synchronized (lock) {
			if (matchingPlayers.isEmpty()) {
				firstGenre = genre;
				firstType = type;
				firstDifficultSelect = difficultSelect;
			}

			// テーマモードの場合は全ジャンル全問題形式から出題する
			if (theme != null) {
				genre = 0;
				type = 0;
			}

			// 書き込み同期はServiceImplで取る
			final PlayerStatus status = new PlayerStatus(name, Game.playerId++, this.arrayPlayerStatus.size(), this.sessionId, true, greeting, imageFileName, classLevel, rating, userCode);

			arrayPlayerStatus.add(status);
			final PacketMatchingPlayer matchingPlayer = new PacketMatchingPlayer();
			matchingPlayer.name = name;
			matchingPlayer.isRequestSkip = false;
			matchingPlayer.greeting = greeting;
			matchingPlayer.imageFileName = imageFileName;
			matchingPlayers.add(matchingPlayer);

			for (int i = 0; i < Constant.MAX_PROBLEMS_PER_PLAYER; ++i) {
				final int problemID = selectProblem(genre, type, classLevel, difficultSelect, theme, newProblem);
				problemIDs.add(problemID);
			}
			setGenre.add(genre);
			setType.add(type);
			setDifficult.add(difficultSelect);

			++numberOfHumanPlayer;
			++initialNumberOfHumanPlayers;

			if (arrayPlayerStatus.size() >= Constant.MAX_PLAYER_PER_SESSION || alone) {
				transToReadyForGame();
			}

			return status;
		}
	}

	private int selectProblem(int genre, int type, int classLevel, int difficultSelect, String theme, boolean newProblem) {
		synchronized (lock) {
			if (event) {
				genre = firstGenre;
				type = firstType;
				difficultSelect = firstDifficultSelect;
			}

			try {
				if (theme == null) {
					final boolean tegaki = event;
					return ProblemManager.getInstance().selectProblemForNormalMode(genre, type, classLevel, difficultSelect, selectedProblemIds, true, newProblem, tegaki).problemId;
				} else {
					// テーマモード
					return ProblemManager.getInstance().selectProblemForThemeMode(theme, difficultSelect, classLevel, selectedProblemIds).problemId;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

			// 問題選択時に問題が発生した場合はランダムに問題を選択する
			return random.nextInt(100000);
		}
	}

	public void requestStartingGame(int playerListId) {
		synchronized (lock) {
			final PlayerStatus status = (PlayerStatus) arrayPlayerStatus.get(playerListId);
			if (status.isRequestStartingGame()) {
				return;
			}

			status.setRequestStartingGame();
			matchingPlayers.get(playerListId).isRequestSkip = true;
			++numberOfRequestStartingGame;

			if (numberOfRequestStartingGame >= arrayPlayerStatus.size()) {
				transToReadyForGame();
			}
		}
	}

	public PacketMatchingPlayer[] getMatchingPlayer() {
		synchronized (lock) {
			if (getTransition() != Constant.TRANSITION_MATCHING && getTransition() != Constant.TRANSITION_READY_FOR_GAME) {
				return null;
			}

			return matchingPlayers.toArray(new PacketMatchingPlayer[0]);
		}
	}

	public int getRestWaitForGameSecond() {
		synchronized (lock) {
			return restWaitForGameSecond;
		}
	}

	private void transToReadyForGame() {
		synchronized (lock) {
			// タイマーと人数制限が同時にかぶった場合の対応策
			if (timerMatching != null) {
				timerMatching.cancel();
				timerMatching = null;
			} else {
				return;
			}

			if (transition >= Constant.TRANSITION_READY_FOR_GAME) {
				return;
			}

			setTransition(Constant.TRANSITION_READY_FOR_GAME);

			gameManager.notifyMatchingCompleted();

			// サーバー統計変更
			gameManager.changeStatics(1, initialNumberOfHumanPlayers, 0, 1, initialNumberOfHumanPlayers);

			final int genre = (setGenre.size() == 1) ? setGenre.iterator().next() : 0;
			final int type = (setType.size() == 1) ? setType.iterator().next() : 0;
			final int difficultSelect = (setDifficult.size() == 1) ? setDifficult.iterator().next() : Constant.DIFFICULT_SELECT_NORMAL;

			while (problemIDs.size() < Constant.MAX_PROBLEMS_PER_SESSION) {
				final int problemID = selectProblem(genre, type, classLevel, difficultSelect, theme, true);
				problemIDs.add(problemID);
			}

			problems = DatabaseFactory.getInstance().getProblemData(problemIDs);

			computerPlayer = new ComputerPlayer(problemIDs);

			// COMプレイヤー追加
			while (arrayPlayerStatus.size() < Constant.MAX_PLAYER_PER_SESSION) {
				final String name = computerPlayer.getName(classLevel);
				final String greeting = computerPlayer.getGreeting();

				final PlayerStatus status = new PlayerStatus(name, -1, arrayPlayerStatus.size(), this.sessionId, false, greeting, comIconFileNames[random.nextInt(comIconFileNames.length)], Constant.MAX_CLASS_LEVEL / 2, 0, -1);
				arrayPlayerStatus.add(status);

				final PacketMatchingPlayer matchingPlayer = new PacketMatchingPlayer();
				matchingPlayer.name = name;
				matchingPlayer.isRequestSkip = false;
				matchingPlayer.greeting = greeting;
				matchingPlayer.imageFileName = status.getImageFileName();
				matchingPlayers.add(matchingPlayer);
			}

			// makeArray();

			restWaitForGameSecond = Constant.WAIT_SECOND_FOR_READY_FOR_GAME;

			timerReadyForGame.schedule(new TimerTask() {
				public void run() {
					synchronized (lock) {
						// ゲーム状態へ遷移
						if (--restWaitForGameSecond < 0) {
							transToQuiz();
						}
					}
				}
			}, 0, 1000);
		}
	}

	// クイズに移行する
	private void transToQuiz() {
		synchronized (lock) {
			if (timerReadyForGame != null) {
				timerReadyForGame.cancel();
				timerReadyForGame = null;
			} else {
				return;
			}

			if (transition >= Constant.TRANSITION_QUESTION) {
				return;
			}

			transition = Constant.TRANSITION_QUESTION;

			checkTempRanking();

			nextQuestion();
		}
	}

	// 残り回答時間を返す
	public int getRestQuestionSecond() {
		synchronized (lock) {
			long currentTime = Calendar.getInstance().getTimeInMillis();
			int rest = (int) (currentTime - questionStartTime);
			rest = Constant.MAX_QUESTION_TIME - rest;
			return rest;
		}
	}

	// 解答情報をセット
	public void receiveAnswer(int playerListId, String answer) {
		synchronized (lock) {
			if (getTransition() != Constant.TRANSITION_QUESTION) {
				return;
			}

			final PlayerStatus player = arrayPlayerStatus.get(playerListId);
			player.clearSkipCount();
			if (player.isAnswered()) {
				return;
			}
			player.setAnswer(answer, Math.max(1, getRestQuestionSecond()));

			// pushAnswer(playerListId, answer);

			if (++answerCounter >= getNumberOfHumanPlayer()) {
				checkAnswer();
			}
		}
	}

	private void nextQuestion() {
		synchronized (lock) {
			// 問題出題に遷移
			transition = Constant.TRANSITION_QUESTION;
			answerCounter = 0;

			for (PlayerStatus player : arrayPlayerStatus) {
				player.incSkipCount();
				player.clear();
			}

			// 時間切れタイマーセット
			questionStartTime = Calendar.getInstance().getTimeInMillis();

			timerQuestion = new Timer();
			// 常に新しいインスタンスを作る必要がある
			timerQuestion.schedule(new TimerTask() {
				public void run() {
					tickQuestionTimer();
				}
			}, 1000, 1000);
		}
	}

	private void tickQuestionTimer() {
		synchronized (lock) {
			if (getRestQuestionSecond() < 0) {
				if (timerQuestion != null) {
					timerQuestion.cancel();
					timerQuestion = null;
					checkAnswer();
				}
			} else {
				final int computerTiming = (classLevel == Constant.CLASS_LEVEL_NORMAL) ? 0 : classLevel;
				if (getRestQuestionSecond() < 10000 + 20000 * computerTiming / Constant.MAX_CLASS_LEVEL) {
					final int index = random.nextInt(Constant.MAX_PLAYER_PER_SESSION);

					final PlayerStatus playerStatus = arrayPlayerStatus.get(index);
					if (!playerStatus.isHuman() && !playerStatus.isAnswered()) {
						final PacketProblemData problem = problems[problemCounter];
						final String answer = computerPlayer.getAnswer(problem, playerStatus.getName(), classLevel);
						playerStatus.setAnswer(answer, getRestQuestionSecond());
					}
				}
			}
		}
	}

	private void checkAnswer() {
		synchronized (lock) {
			if (timerQuestion != null) {
				timerQuestion.cancel();
				timerQuestion = null;
			}

			answerCounter = 0;

			transition = Constant.TRANSITION_ANSWER;

			// COM回答作成
			// HUM再度解答送信
			// PacketProblemData problem = (PacketProblemData)
			// problems[problemCounter];
			final PacketProblemData problem = problems[problemCounter];

			for (PlayerStatus player : arrayPlayerStatus) {
				if (!player.isHuman() && !player.isAnswered()) {
					String answer = computerPlayer.getAnswer(problem, player.getName(), classLevel);
					int restTime = random.nextInt(Math.max(1, getRestQuestionSecond()));
					player.setAnswer(answer, restTime);
					// pushAnswer(player.getPlayerListId(), answer);
				}

				if (player.isHuman() && player.isAnswered()) {
					player.clearSkipCount();
				}
			}

			// 全員の正解チェック
			final List<String> playerAnswers = new ArrayList<String>();
			for (PlayerStatus player : arrayPlayerStatus) {
				final boolean correct = problem.isCorrect(player.getAnswer());
				if (correct) {
					final int point = calcPoint(problem, player.getClassLevel(), player.getTimeRemain());
					player.addScore(point);
				}

				if (player.isHuman()) {
					if (correct) {
						++problem.good;
					} else {
						++problem.bad;
					}

					if (player.getAnswer() != null && !player.getAnswer().isEmpty()) {
						playerAnswers.add(player.getAnswer());
					}
				}
			}

			if (!playerAnswers.isEmpty()) {
				ThreadPool.getInstance().execute(new Runnable() {
					public void run() {
						DatabaseFactory.getInstance().addPlayerAnswers(problem.problemId, problem.type, playerAnswers);
					}
				});
			}

			// データベース更新
			// テーマモードだった場合は正解率を更新しない
			if (theme == null) {
				ThreadPool.getInstance().execute(new Runnable() {
					public void run() {
						ProblemManager.getInstance().updateMinimumProblemData(problem);
					}
				});
			}

			// 回線落ち判定
			for (PlayerStatus player : arrayPlayerStatus) {
				if (player.isHuman() && player.isDrop()) {
					player.dropPlayer();
					--numberOfHumanPlayer;
				}
			}

			// 途中順位決定
			checkTempRanking();

			// 問題数カウント
			++problemCounter;

			// MAX_ANSWER_TIME間解答表示
			timerEndAnswer = new Timer();
			// 常に新しいインスタンスを入れる必要がある
			timerEndAnswer.schedule(new TimerTask() {
				public void run() {
					synchronized (lock) {
						onEndAnswer();
					}
				}
			}, Constant.MAX_ANSWER_TIME);
		}
	}

	private int calcPoint(PacketProblemData problemData, int classLevel, int restTime) {
		if (theme == null) {
			// 通常モード
			final int maxQuestionTime = Constant.MAX_QUESTION_TIME;
			final int maxClassLevel = Constant.MAX_CLASS_LEVEL;
			final double rc = (double) (maxClassLevel - classLevel) / (double) maxClassLevel;
			final int perfectBoderTime = (int) (Constant.MAX_PERFECT_BORDER_TIME * rc);
			final int perfectPoint = Constant.MAX_POINT / Constant.MAX_PROBLEMS_PER_SESSION;

			if (restTime + perfectBoderTime > maxQuestionTime) {
				return perfectPoint;
			}

			final double r = (double) restTime / (double) (maxQuestionTime - perfectBoderTime);
			final int compressPoint = (int) (perfectPoint * ((Constant.MAX_POINT_COMPRESS - Constant.MIN_POINT_COMPRESS) * rc + Constant.MIN_POINT_COMPRESS));
			final int point = (int) (perfectPoint * r + compressPoint * (1.0 - r));
			return point;
		} else {
			// 検定モード
			int correctRatio = problemData.getCorrectRatio();
			if (correctRatio < 0) {
				correctRatio = 50;
			}
			final double scale = 4.0 - 0.03 * (double) correctRatio;
			final double basePoint = scale * Constant.MAX_POINT / Constant.MAX_PROBLEMS_PER_SESSION;

			// 残りの部分は通常モードの採点方式ほとんど同じ
			final int maxQuestionTime = Constant.MAX_QUESTION_TIME;
			final int maxClassLevel = Constant.MAX_CLASS_LEVEL;
			final double rc = (double) (maxClassLevel - classLevel) / (double) maxClassLevel;
			final int perfectBoderTime = (int) (Constant.MAX_PERFECT_BORDER_TIME * rc);

			if (restTime + perfectBoderTime > maxQuestionTime) {
				return (int) basePoint;
			}

			final double r = (double) restTime / (double) (maxQuestionTime - perfectBoderTime);
			final int compressPoint = (int) (basePoint * ((Constant.MAX_POINT_COMPRESS - Constant.MIN_POINT_COMPRESS) * rc + Constant.MIN_POINT_COMPRESS));
			final int point = (int) (basePoint * r + compressPoint * (1.0 - r));
			return point;
		}
	}

	private void onEndAnswer() {
		synchronized (lock) {
			if (timerEndAnswer != null) {
				timerEndAnswer.cancel();
				timerEndAnswer = null;
			}

			if (problemCounter >= Constant.MAX_PROBLEMS_PER_SESSION) {
				transToResult();
			} else {
				nextQuestion();
			}
		}
	}

	private static final double K = 32;

	private void transToResult() {
		synchronized (lock) {
			// 結果表示に遷移
			transition = Constant.TRANSITION_RESULT;

			// 順位決定
			final PlayerStatus players[] = arrayPlayerStatus.toArray(new PlayerStatus[0]);
			Arrays.sort(players, new Comparator<PlayerStatus>() {
				public int compare(PlayerStatus o1, PlayerStatus o2) {
					return o2.getScore() - o1.getScore();
				}
			});

			// レーティング計算
			final int[] ratingDeltas = new int[players.length];
			for (int i = 0; i < players.length; ++i) {
				final PlayerStatus playerA = players[i];
				playerA.setRank(i + 1);

				if (!playerA.isHuman()) {
					continue;
				}

				double sa = 0;
				double ea = 0;

				for (int j = 0; j < players.length; ++j) {
					final PlayerStatus playerB = players[j];
					if (i == j || !playerB.isHuman()) {
						continue;
					}

					if (i < j) {
						// AがBに勝った場合
						sa += 1.0;
					}

					ea += 1.0 / (1.0 + Math.pow(10.0, (double) (playerB.getRating() - playerA.getRating()) / 400.0));
				}

				ratingDeltas[i] = (int) (K * (sa - ea));
			}

			for (int i = 0; i < players.length; ++i) {
				players[i].setRating(ratingDeltas[i]);
			}

			packetResult = new PacketResult[players.length];
			for (int i = 0; i < players.length; ++i) {
				packetResult[i] = players[i].toResult();
			}

			if (theme != null) {
				for (PlayerStatus playerStatus : players) {
					final int userCode = playerStatus.getUserCode();
					if (userCode < 0) {
						continue;
					}

					final int score = playerStatus.getScore();
					DatabaseFactory.getInstance().updateThemeModeScore(userCode, theme, score);
				}
			}

			ThreadPool.getInstance().getService().schedule(runnableRemoveSession, 1, TimeUnit.MINUTES);
		}
	}

	private final Runnable runnableRemoveSession = new Runnable() {
		public void run() {
			gameManager.changeStatics(-1, -initialNumberOfHumanPlayers, 0, 0, 0);
			gameManager.removeSession(sessionId);
		}
	};

	// 回線が生きていることを通知する
	public void keepAlive(int playerListId) {
		synchronized (lock) {
			if (playerListId >= 0) {
				arrayPlayerStatus.get(playerListId).clearSkipCount();
			}
		}
	}

	// 現在何問目かを返す
	public int getProblemNumber() {
		synchronized (lock) {
			return problemCounter;
		}
	}

	// プレイヤー情報を直接返す
	public PlayerStatus getPlayerStatus(int playerListId) {
		synchronized (lock) {
			return arrayPlayerStatus.get(playerListId);
		}
	}

	public PacketResult[] getPacketResult() {
		synchronized (lock) {
			return packetResult;
		}
	}

	public PacketGameStatus getGameStatus(int playerListId) {
		synchronized (lock) {
			final PacketGameStatus status = new PacketGameStatus();
			if (playerListId >= 0) {
				status.playerScore = arrayPlayerStatus.get(playerListId).getScore();
			}
			status.problemCounter = getProblemNumber();
			status.restSecond = getRestQuestionSecond();
			status.transition = getTransition();
			status.playerAnswers = new String[arrayPlayerStatus.size()];
			status.ranking = new int[arrayPlayerStatus.size()];
			for (int i = 0; i < arrayPlayerStatus.size(); ++i) {
				status.playerAnswers[i] = arrayPlayerStatus.get(i).getAnswer();
				status.ranking[i] = arrayPlayerStatus.get(i).getTempRanking();
			}

			for (PlayerStatus playerStatus : arrayPlayerStatus) {
				if (playerStatus.isHuman() && !playerStatus.isDrop()) {
					++status.numberOfPlayingHumans;
				}
			}

			return status;
		}
	}

	public String[] getPlayerNameList() {
		synchronized (lock) {
			final List<String> list = new ArrayList<String>();
			for (PlayerStatus player : arrayPlayerStatus) {
				list.add(player.getName());
			}
			return list.toArray(new String[0]);
		}
	}

	public int getSessionId() {
		synchronized (lock) {
			return sessionId;
		}
	}

	private void checkTempRanking() {
		// 順位決定
		final PlayerStatus[] players = arrayPlayerStatus.toArray(new PlayerStatus[0]);
		Arrays.sort(players, new Comparator<PlayerStatus>() {
			public int compare(PlayerStatus o1, PlayerStatus o2) {
				return o2.getScore() - o1.getScore();
			}
		});
		for (int i = 0; i < players.length; ++i) {
			players[i].setTempRanking(i + 1);
		}
	}

	public boolean isEvent() {
		return event;
	}

	public boolean isPublicEvent() {
		return publicEvent;
	}

	public int getFirstGenre() {
		return firstGenre;
	}

	public int getFirstType() {
		return firstType;
	}
}
